using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Etapa1.ComercioEspacial
{
	using ProximityDatabase = Bnoerj.AI.Steering.IProximityDatabase<Bnoerj.AI.Steering.IVehicle>;
	using ProximityToken = Bnoerj.AI.Steering.ITokenForProximityDatabase<Bnoerj.AI.Steering.IVehicle>;
    using PloobsEngine.Utils;
    using Bnoerj.AI.Steering;
    
	public class NaveVeiculo : SimpleVehicle
	{        
		// constructor
        public NaveVeiculo(ProximityDatabase pd, PolylinePathway path, List<SphericalObstacle> obstacles)
		{
            this.path = path;
            if (path == null)
                ispathFollow = false;
            else
                ispathFollow = true;
            this.obstacles = obstacles;

			// allocate a token for this boid in the proximity database
			proximityToken = null;
			NewPD(pd);            

			// reset Pedestrian state
			Reset();
		}


        public float planetRadius = 0;

		// reset all instance state
		public override void Reset()
		{
			// reset the vehicle 
			base.Reset();

			// max speed and max steering force (maneuverability) 
			MaxSpeed = 5.0f;
			MaxForce = 3.0f;

			// initially stopped
			Speed = 0;

			// size of bounding sphere, for obstacle avoidance, etc.
			Radius = 5f; // width = 0.7, add 0.3 margin, take half            

			// randomize 2D heading
			RandomizeHeadingOnXZPlane();

			
			// notify proximity database that our position has changed
			if (proximityToken != null) proximityToken.UpdateForNewPosition(Position);
		}

		// per frame simulation update
		public void Update(float currentTime, float elapsedTime)
		{
			// apply steering force to our momentum
			ApplySteeringForce(DetermineCombinedSteering(elapsedTime), elapsedTime);            			
			
			// notify proximity database that our position has changed
			proximityToken.UpdateForNewPosition(Position);            
		}

		// compute combined steering force: move forward, avoid obstacles
		// or neighbors if needed, otherwise follow the path and wander
		public Vector3 DetermineCombinedSteering(float elapsedTime)
		{
			// move forward
			Vector3 steeringForce = Vector3.Zero;

			// probability that a lower priority behavior will be given a
			// chance to "drive" even if a higher priority behavior might
			// otherwise be triggered.
			const float leakThrough = 0.5f;

			// determine if obstacle avoidance is required
			Vector3 obstacleAvoidance = Vector3.Zero;
            if (leakThrough < StaticRandom.Random())
			{
				const float oTime = 6; // minTimeToCollision = 6 seconds
                obstacleAvoidance = SteerToAvoidObstacles(oTime, obstacles);
			}

			// if obstacle avoidance is needed, do it
			if (obstacleAvoidance != Vector3.Zero)
			{
				steeringForce += obstacleAvoidance;
			}
			else
			{
				// otherwise consider avoiding collisions with others
				Vector3 collisionAvoidance = Vector3.Zero;
				const float caLeadTime = 3;

				// find all neighbors within maxRadius using proximity database
				// (radius is largest distance between vehicles traveling head-on
				// where a collision is possible within caLeadTime seconds.)
				float maxRadius = caLeadTime * MaxSpeed * 2;
				neighbors.Clear();
				proximityToken.FindNeighbors(Position, maxRadius, ref neighbors);

                if (neighbors.Count > 0 && leakThrough < StaticRandom.Random())
					collisionAvoidance = SteerToAvoidNeighbors(caLeadTime, neighbors) * 15;

				// if collision avoidance is needed, do it
				if (collisionAvoidance != Vector3.Zero)
				{
					steeringForce += collisionAvoidance;
				}
				else
				{	    
					steeringForce += SteerForWander(elapsedTime) ;

                    if (ispathFollow)
                    {                        

                        if (Vector3.Distance(Position, path.points[path.pointCount - 1]) < planetRadius * 1.5f)
                        {
                            ispathFollow = false;

                        }
                        else
                        {
                            const float pfLeadTime = 5;
                            Vector3 pathFollow = SteerToFollowPath(1, pfLeadTime, path);
                            steeringForce += pathFollow * 2f;
                        }
                    }
				}
			}			
            
			return steeringForce;
		}


		public void NewPD(ProximityDatabase pd)
		{
			// delete this boid's token in the old proximity database
			if (proximityToken != null)
			{
				proximityToken.Dispose();
				proximityToken = null;
			}

			// allocate a token for this boid in the proximity database
			proximityToken = pd.AllocateToken(this);
		}


		ProximityToken proximityToken;
		static List<IVehicle> neighbors = new List<IVehicle>();
        PolylinePathway path;
        List<SphericalObstacle> obstacles;
        bool ispathFollow = false;

        public bool IspathFollow
        {
            get { return ispathFollow; }
            set { ispathFollow = value; }
        }        

        public PolylinePathway Path
        {
            get { return path; }
            set { path = value; }
        }
		
	}
}
